//
//      Copyright Timothy Evans 2014 2015    timothy.evans@gsecraif.org
//
//      This file is part of gsecraif.
//
//      gsecraif is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 3 of the License, or
//      (at your option) any later version.
//
//      See gsecraif.cpp
//
// =====================================================================
//
//! Functions to split file.
//
// =====================================================================
//
// Compile with g++

#include "split_functions.h"

using namespace std;

extern unsigned short int debuglevel;
extern bool showhashes;

void split_input(int numoffiles, unsigned short int rrotate, bool transpose,   
                 vector<string> &compfilenames) {

unsigned int bytescount = 0;            // how many bytes have been read,
                                        // progress counter
unsigned long int totalbytes=0;         // grand total of bytes read
const char *std_in = "/dev/stdin";      // reference to Standard In
string postfix = "000";                 // 3 character postfix for component
                                        // file names
string compfilename;                    // component file name - prefixpostfix
ofstream outfile[255];                  // component files for output

char fileformatversion=1;               // component file format version
                                        // supported by this programme
int bytestoread = numoffiles - 1;       // how many bytes need to be read at
                                        // a time,
                                        // one less than number of files
                                        // there is the parity byte to write
                                        // as well
vector<unsigned char> bytes;            // array of bytes read in
vector<unsigned char> bit_rotatedbytes; // array of bit rotated bytes
Stripe stripe1(numoffiles);             // array of bytes and parity byte
bool finished = false;                  // flag to show if finished
                                        // reading / writing data
bool eofile = false;                    // flag for end of file
bool nullstripe = false;                // flag for null stripe
                                        // when eof is at beginning of stripe
                                        // ie no padding needed
unsigned short int padbytes = 0;        // how many padding bytes there are
unsigned short int stripestart = 0;     // where in the "array" the
                                        // stripe starts
unsigned char parity_byte=0;            // parity value
bytestoread = numoffiles -1;            // number of bytes to read

// Initialize the vectors:
for (int compfileid=0; compfileid != (numoffiles - 1); compfileid++) {
    bytes.push_back(0);
    bit_rotatedbytes.push_back(0);
    }

// Is transpose used?
if (transpose) {
	fileformatversion=2;
	rrotate=0;
    }

// open standard in:
ifstream in(std_in, ios::in | ios::binary); // input
if(!in) {
    if (debuglevel) cerr << ERR_STDIN;
    return;  
    }


// open output files:
for (short unsigned int compfileid=0; compfileid <= numoffiles-1; ++compfileid) {
    // Note: open() function needs C style string for file name
    // hence .c_str()	
    outfile[compfileid].open(compfilenames[compfileid].c_str(),
                             ios::out | ios::binary);
    // output
    if(!outfile[compfileid]) {
        if (debuglevel) cerr << ERR_OPEN_OUT_FILE << outfile[compfileid] << "\n";
        return;
        }
    } // for (short...


// output file headers:
for (short unsigned int compfileid=0; compfileid <= numoffiles-1; ++compfileid) {
    outfile[compfileid].put(fileformatversion);	// file format version
    outfile[compfileid].put(numoffiles);        // number of component files
    outfile[compfileid].put(compfileid);        // identity of file
    outfile[compfileid].put(rrotate);           // rotation
    }

// output the data, loop until finished flag is set:
while (!finished) {
    // read bytes:
    read_orig_bytes(bytestoread, bytes, eofile, finished, 
                    bytescount, padbytes, nullstripe, in);

    // track progress:
    if (bytescount > 1024) {
        totalbytes += 1024;
        bytescount = bytescount % 1024;
        if (showhashes) cerr << "#";
        } // if (bytescount...

    if (!nullstripe) {
		if (!transpose) {
            // Bit rotate the bytes: 
            bit_rotate(bytes, bit_rotatedbytes, bytestoread, rrotate);
		    }
		else {
			bit_transpose(bytes, bit_rotatedbytes, bytestoread, false);
		    }                    
        stripe1.slice_to_stripe(bit_rotatedbytes); // build stripe
        stripe1.rotate_stripe(stripestart); //rotate the stripe
        // write stripe:
        for ( int compfileid=0; compfileid<=(numoffiles-1); ++compfileid) {
            // note use of numoffiles - not numoffiles - 1
            // because stripe includes the parity byte
            outfile[compfileid].put(stripe1.get_byte(compfileid));            	
            }
        // advance the start of the stripe, and rotate to begining if necessary:
        ++stripestart;
        if (stripestart > (numoffiles-1)) stripestart = 0;
        } //   if (!nullstripe)
        
    } // while (!finished...

totalbytes += bytescount;  // update the total bytes

// write out the trailer:
for (int compfileid=0; compfileid<=(numoffiles-1); ++compfileid) {
    outfile[compfileid].put(padbytes);
    }
// close the input file:
in.close();
// close the output files:
for (short unsigned int compfileid=0; compfileid <= numoffiles-1; ++compfileid) {
    outfile[compfileid].close();
    }

// Print some basic information if debug level > 0 :
if (debuglevel) {
    cerr << "\n";
    cerr << totalbytes << " Bytes read.\n";
    cerr << "Input has been split.\n";
    }

// file has been split
} // end of split_input function

// =====================================================================


// read slice from original file to split
void read_orig_bytes(int bytestoread, vector<unsigned char> &bytes,
                     bool &eofile, bool &finished, unsigned int &bytescount, 
                     unsigned short int &padbytes, bool &nullstripe,
                     ifstream &in)                     
                     
{
char ch;
// read bytes
// array goes from 0 so use bytestoread-1
for (unsigned short int bytex=0; bytex <= (bytestoread-1); ++bytex) {
    if (!eofile) {
        in.get(ch);
        if(in) {
            bytes[bytex] = ch;
            ++bytescount;
            } // if(in)...
        // if the end of file is reached part way through a slice, pad the rest
        // if the first byte is EOF this slice is not needed
        else { // if(in)...
            if (bytex>0) {
                bytes[bytex]=rand()%255;
                ++padbytes; 
                }
            else {
                nullstripe = true;
                } // if (bytex>0 ...
                
            eofile = true;
            finished = true;
            } // if(in)...

        } // if (!eofile...
    else {
        // eofile is true so we pad the rest
        if (!nullstripe) {
            bytes[bytex]=rand()%255;
            ++padbytes;     
            }
            
        } // if (!eofile...
    } // for (bytex=0...

}
// =====================================================================
